﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using RawData;
using System.Web;
using SqlShareAPI;
using Westwind.Web.JsonSerializers;
using System.Xml.Serialization;

namespace WebQueryRole
{
    /// <summary>
    /// Summary description for restapi
    /// </summary>
    public class restapi : IHttpHandler
    {
        static restapi()
        {
            _restMethods = new List<RestMethod>();
            _restMethods.Add(new TestMethod());
            _restMethods.Add(new FileUploadMethod());
        }

        private static List<RestMethod> _restMethods;

        public void ProcessRequest(HttpContext context)
        {
            
            context.Response.ContentType = "text/plain";
            ResponseObject ro = new ResponseObject();
            string responseFormat = GetRequiredParam("reponseFormat", context, "normal");

            // get this before the try so we can finally close it in the end
            SSAPIFactory factory = CloudUtil.CreateSSAPIFactory();
            try
            {
                // parse method
                string methodName = GetRequiredParam("method", context);
                string username = GetRequiredParam("username", context);
                string password = GetRequiredParam("password", context);

                // get user credentials
                UserSession userSession = factory.CreateUserSessionViaPassword(username, password);

                // try to find result type                
                foreach (RestMethod method in _restMethods)
                {
                    if (method.MethodName.Equals(methodName))
                    {
                        ro.result = method.processRequest(context, userSession);
                        ro.success = true;
                        break;
                    }
                }

                if (!ro.success)
                {
                    throw new MissingMethodException("param 'method' = " + methodName + " no such method");
                }                
            }
            catch (Exception e)
            {
                ro.success = false;
                ro.result = new ResponseException(e);             
            }
            finally
            {
                factory.Close();
            }

            WriteResponse(context, ro, responseFormat);
        }

        private void WriteResponse(HttpContext context, ResponseObject ro, string responseFormat)
        {
            if ("normal".Equals(responseFormat))
            {
                context.Response.ContentType = "text/plain";                
                context.Response.Write(ro.result);  // this should write the exception if it was failed as well    
            }/*
            else if ("xml".Equals(responseFormat))
            {
                context.Response.ContentType = "text/xml";     
                XmlSerializer ser = new XmlSerializer(typeof(ResponseObject));
                ser.Serialize(context.Response.OutputStream, ro);
            }*/
            else if ("json".Equals(responseFormat))
            {
                context.Response.ContentType = "application/json";
                JSONSerializer ser = new JSONSerializer();
                context.Response.Write(ser.Serialize(ro));
            }
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }

        /// <summary>
        ///  Internal interface for creating Rest Methods inside this class
        /// </summary>
        interface RestMethod
        {
            string MethodName { get; }
            Object processRequest(HttpContext context, UserSession userSession);
        }

        
        public class ResponseObject
        {
            public ResponseObject()
            {
                success = false;
                result = null;
            }
            public bool success { get; set; }            
            public Object result { get; set; }
            //public ResponseException exception { get; set; }
            
        }
        
        public class ResponseException
        {
            public ResponseException() { }
            public ResponseException(Exception e)
            {
                ErrorMsg = e.Message;
                StackTrace = e.StackTrace;
            }

            public override string ToString()
            {
                return ErrorMsg + Environment.NewLine + StackTrace;
            }

            public string ErrorMsg { get; set; }
            public string StackTrace { get; set; }
        }


        /// <summary>
        ///  Get the required param, throw an error if we did not get what we needed or if string is empty
        /// </summary>
        /// <param name="param"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        internal static string GetRequiredParam(string param, HttpContext context)
        {
            return GetRequiredParam(param, context, null);
        }

        // <summary>
        /// Get required param, if defaulted = null, throw an error, otherwise return the default value
        /// </summary>
        /// <param name="param"></param>
        /// <param name="context"></param>
        /// <param name="default"></param>
        /// <returns></returns>
        internal static string GetRequiredParam(string param, HttpContext context, string defaulted)
        {
            string str = context.Request[param];
            if (string.IsNullOrEmpty(str))
            {
                if (defaulted != null)
                {
                    return defaulted;
                }
                throw new NullReferenceException("Invalid required param: " + param);
            }
            return str;
        }

        class TestMethod : RestMethod
        {            
            string RestMethod.MethodName
            {
                get { return "test"; }
            }

            object RestMethod.processRequest(HttpContext context, UserSession userSession)
            {
                return "Hello World " + userSession.FullName;
            }

        }

        class FileUploadMethod : RestMethod
        {
            string RestMethod.MethodName
            {
                get { return "fileupload"; }
            }

            object RestMethod.processRequest(HttpContext context, UserSession userSession)
            {
                string delStr = GetRequiredParam("delimiter", context);
                string hasColumnsStr = GetRequiredParam("hasHeader", context);
                string tablename = GetRequiredParam("tablename", context);                

                char delimiter = (delStr.Equals("tab") ? '\t' : delStr.ToCharArray()[0]);
                bool hasHeaders = bool.Parse(hasColumnsStr);

                MemoryStream localStream = new MemoryStream();
                DataFileReader dataAnalyzer = new DataFileReader(tablename, delimiter, hasHeaders);
                SqlBatchFileUploader batchUploader = new SqlBatchFileUploader(dataAnalyzer.SQLTable, 4000, hasHeaders);

                if (batchUploader.TableExist(userSession.ContentSqlConnection))
                {                    
                    throw new Exception("Table Already exists, please choose another Table name");
                }                

                // upload file
                SaveFile(context.Request.Files[0].InputStream, localStream);

                // analyze
                dataAnalyzer.ExtractMetadata(localStream, false);

                // dump into DB
                batchUploader.BulkCopyFile(localStream, userSession.ContentSqlConnection, false);

                // create meta record in meta database
                userSession.CreateTable(batchUploader.TableName, "", false, false);

                return "true";                
            }

            private void SaveFile(Stream stream, MemoryStream ms)
            {
                byte[] buffer = new byte[4096];
                int bytesRead;
                while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) != 0)
                {
                    ms.Write(buffer, 0, bytesRead);
                }
            } 
        }
    }
}